/*
连接：https://llfc.club/articlepage?id=2R2LjXaJqlXvTqHLY4034ojk5pa

只初始化一个iocontext用来监听服务器的读写事件，包括新连接到来的监听也用这个iocontext。
只是我们让iocontext.run在多个线程中调用，这样回调函数就会被不同的线程触发，从这个角度
看回调函数被并发调用了。


IOThreadPool模式有一个隐患，同一个socket的就绪后，触发的回调函数可能在不同的线程里，比如第一次是在线程1，
第二次是在线程3，如果这两次触发间隔时间不大，那么很可能出现不同线程并发访问数据的情况，比如在处理读事件时，
第一次回调触发后我们从socket的接收缓冲区读数据出来，第二次回调触发,还是从socket的接收缓冲区读数据，就会
造成两个线程同时从socket中读数据的情况，会造成数据混乱。

利用strand改进
对于多线程触发回调函数的情况，我们可以利用asio提供的串行类strand封装一下，这样就可以被串行调用了，其基本
原理就是在线程各自调用函数时取消了直接调用的方式，而是利用一个strand类型的对象将要调用的函数投递到strand
管理的队列中，再由一个统一的线程调用回调函数，调用是串行的，解决了线程并发带来的安全问题。
*/

#ifndef ASIO_IO_THREAD_POOL_H
#define ASIO_IO_THREAD_POOL_H

#include "singleton.h"

#include <utility>
#include <memory>
#include <vector>
#include <thread>

#include <boost/asio.hpp>

class IOThreadPool : public Singleton<IOThreadPool>
{
    friend class Singleton<IOThreadPool>;
public:
    using IOContext = boost::asio::io_context;

    ~IOThreadPool();
    IOThreadPool(const IOThreadPool&) = delete;
    IOThreadPool& operator=(const IOThreadPool&) = delete;

    IOContext& GetIOContext();
    void Stop();

private:
    IOThreadPool(uint32_t threadNum = std::thread::hardware_concurrency());

private:
    std::vector<std::thread> m_threads;
    IOContext m_ctx;
    std::unique_ptr<boost::asio::io_context::work> m_work;  // 防止ctx被销毁
};


#endif // ASIO_IO_THREAD_POOL_H